#include "CDataObject.h"
#include "EnumFE.h"
CDataObject::CDataObject()
{
	m_pFormatEtc = NULL;
	m_pStgMedium = NULL;
	m_cDataCount = 0;
}

CDataObject::~CDataObject()
{
	if(m_pFormatEtc)
		delete m_pFormatEtc;
	if(m_pStgMedium)
	{
		DWORD i;
		for(i = 0; i < m_cDataCount; i++)
			ReleaseStgMedium(&m_pStgMedium[i]);
		delete m_pStgMedium;
	}
}


HRESULT CDataObject::QueryInterface(REFIID riid, LPVOID *ppv)
{
	*ppv = NULL;

	//IUnknown
	if(IsEqualIID(riid, IID_IUnknown))
	{
		*ppv = static_cast<IUnknown*>(this);
		AddRef();
		return NOERROR;
	}
	else if(IsEqualIID(riid, IID_IDataObject))
	{
		*ppv = static_cast<IDataObject*>(this);
		AddRef();
		return NOERROR;
	}

	return E_NOINTERFACE;
}

/**************************************************************************

   CDataObject::AddRef()

**************************************************************************/

ULONG CDataObject::AddRef()
{
	return CRefCount::AddRef();
}


ULONG CDataObject::Release()
{
	return CRefCount::Release();
}

HRESULT CDataObject::GetData(LPFORMATETC pFE, LPSTGMEDIUM pSM)
{
	if(pFE == NULL || pSM == NULL)
		return E_INVALIDARG;
	if(m_cDataCount > 0 && (m_pFormatEtc == NULL || m_pStgMedium == NULL))
		return E_FAIL;

	ZeroMemory(pSM, sizeof(STGMEDIUM));

	DWORD i;

	for(i = 0; i < m_cDataCount; i++)
	{
		if((pFE->tymed & m_pFormatEtc[i].tymed) && 
         (pFE->dwAspect == m_pFormatEtc[i].dwAspect) && 
         (pFE->cfFormat == m_pFormatEtc[i].cfFormat) &&
         (pFE->lindex == m_pFormatEtc[i].lindex))
		 {
			HRESULT  hr;
			hr = CopyStgMedium(&m_pStgMedium[i], pSM);
			if(pSM->tymed == TYMED_HGLOBAL)
			{
				//this tell's the caller not to free the global memory
				QueryInterface(IID_IUnknown, (LPVOID*)&pSM->pUnkForRelease);
			}
			return hr;
		}
	 }
	 return DV_E_FORMATETC;
}

HRESULT CDataObject::GetDataHere(LPFORMATETC pFE, LPSTGMEDIUM pSM)
{
	return E_NOTIMPL;
}

HRESULT CDataObject::QueryGetData(LPFORMATETC pFE)
{ 
	if(!pFE)
		return DV_E_FORMATETC;
	if(!(DVASPECT_CONTENT & pFE->dwAspect))
		return DV_E_DVASPECT;
	if(m_cDataCount > 0 && m_pFormatEtc == NULL)
		return E_FAIL;

	DWORD    i;
	HRESULT  hr = DV_E_TYMED;

	for(i = 0; i < m_cDataCount; i++)
	{
		if(pFE->tymed & m_pFormatEtc[i].tymed)
		{
			if(pFE->cfFormat == m_pFormatEtc[i].cfFormat)
				return S_OK;
			else
				hr = DV_E_CLIPFORMAT;
		}
		else
		{
			hr = DV_E_TYMED;
		}
	 }

	return hr;
}

HRESULT CDataObject::GetCanonicalFormatEtc(LPFORMATETC pFE1, LPFORMATETC pFE2)
{
	return DATA_S_SAMEFORMATETC;
}

HRESULT CDataObject::SetData(LPFORMATETC pFE , LPSTGMEDIUM pSM, BOOL fRelease)
{
	LPFORMATETC pfeNew;
	LPSTGMEDIUM psmNew;
	m_cDataCount++;

	pfeNew = new FORMATETC[m_cDataCount];
	psmNew = new STGMEDIUM[m_cDataCount];

	if(pfeNew && psmNew)
    {
		ZeroMemory(pfeNew, sizeof(FORMATETC) * m_cDataCount);
		ZeroMemory(psmNew, sizeof(STGMEDIUM) * m_cDataCount);
   
		//copy the existing data
		if(m_pFormatEtc)
		{
			CopyMemory(pfeNew, m_pFormatEtc, sizeof(FORMATETC) * (m_cDataCount - 1));
		}
		if(m_pStgMedium)
		{
			CopyMemory(psmNew, m_pStgMedium, sizeof(STGMEDIUM) * (m_cDataCount - 1));
		}
   
		//add the new data
		pfeNew[m_cDataCount - 1] = *pFE;
		if(fRelease)
		{
			psmNew[m_cDataCount - 1] = *pSM;
		}
		else
		{
			CopyStgMedium(pSM, &psmNew[m_cDataCount - 1]);
		}

		LPFORMATETC pfeTemp = m_pFormatEtc;
		m_pFormatEtc = pfeNew;
		pfeNew = pfeTemp;

		LPSTGMEDIUM psmTemp = m_pStgMedium;
		m_pStgMedium = psmNew;
		psmNew = psmTemp;
	}

	if(pfeNew)
		delete [] pfeNew;

	if(psmNew)
		delete [] psmNew;

	if(m_pFormatEtc && m_pStgMedium)
		return S_OK;

	return E_OUTOFMEMORY;
}

HRESULT CDataObject::EnumFormatEtc(DWORD dwDir, LPENUMFORMATETC *ppEnum)
{
	*ppEnum = NULL;

	switch(dwDir)
    {
		case DATADIR_GET:
        {
			*ppEnum = new CEnumFormatEtc(m_pFormatEtc, m_cDataCount);
			if(*ppEnum)
				return S_OK;
	        break;
		}
		default:
			return OLE_S_USEREG;
	}

	return E_OUTOFMEMORY;
}

HRESULT CDataObject::DAdvise(  LPFORMATETC pFE, 
                                    DWORD advf, 
                                    LPADVISESINK pAdvSink, 
                                    LPDWORD pdwConnection)
{
	return E_NOTIMPL;
}

HRESULT CDataObject::DUnadvise(DWORD dwConnection)
{
	return E_NOTIMPL;
}

HRESULT CDataObject::EnumDAdvise(LPENUMSTATDATA *ppenumAdvise)
{
	return OLE_E_ADVISENOTSUPPORTED;
}

